package serialer

import (
	"errors"
	"io"
	"strconv"
	"strings"
	"time"

	"github.com/jacobsa/go-serial/serial"
)

// Open 打开串口
func Open(options string) (conn io.ReadWriteCloser, err error) {
	opt := serial.OpenOptions{MinimumReadSize: 0} //设置为0秒，默认10秒超时，10秒内必须有数据
	opt.PortName, opt.BaudRate, opt.ParityMode, opt.DataBits,
		opt.StopBits, opt.InterCharacterTimeout, err = FormatOption(options)
	if err != nil {
		return
	}

	//	opt.Rs485Enable = false
	//	opt.RTSCTSFlowControl = false
	//	opt.Rs485RxDuringTx = true
	//	opt.Rs485RtsHighAfterSend = true
	//	opt.Rs485RtsHighDuringSend = true

	//opt.InterCharacterTimeout = 0 //20000
	//opt.MinimumReadSize = 1       //1
	conn, err = serial.Open(opt)
	//log.Printf("%+v\n", opt)
	return
}

// ReadTimeout 读取数据直到超时
func ReadTimeout(conn io.Reader, data []byte, timeout int) (num int, err error) {
	n := 0
	num = 0
	length := len(data)
	t := time.Now()
	for num < length && timeout > 0 {
		n, err = conn.Read(data[num:])
		if err != nil && err.Error() != "EOF" {
			return num, err
		}
		if n == 0 {
			t1 := int(time.Now().Sub(t) / time.Millisecond)
			if t1 >= timeout {
				err = errors.New("timeout")
				break
			} else if t1+50 > timeout {
				time.Sleep(time.Duration(timeout-t1) * time.Millisecond)
			} else {
				time.Sleep(time.Millisecond * 50)
			}
			continue
		}
		num += n
	}
	return
}

// FormatOption 格式化选项
func FormatOption(opt string) (name string, baud uint, parity serial.ParityMode,
	dataBits uint, stopBits uint, readTimeout uint, err error) {
	readTimeout = 10000 //ms
	opt = strings.Replace(opt, " ", "", -1)
	s := strings.Split(opt, ",")
	if len(s) < 5 {
		err = errors.New("Serial option is error")
		return
	}
	name = s[0]
	ival, err := strconv.Atoi(s[1])
	if err != nil {
		return
	}
	baud = uint(ival) //9600
	ival, err = strconv.Atoi(s[3])
	if err != nil {
		return
	}
	dataBits = uint(ival)
	switch s[2] {
	case "n", "N":
		parity = serial.PARITY_NONE
	case "e", "E":
		parity = serial.PARITY_EVEN
	case "o", "O":
		parity = serial.PARITY_ODD
	default:
		parity = serial.PARITY_NONE
	}
	switch s[4] {
	case "1":
		stopBits = 1
	case "2":
		stopBits = 2
	default:
		stopBits = 1
	}

	if len(s) > 5 {
		timeout, err := strconv.Atoi(s[5])
		if err == nil {
			if timeout > 100 {
				if timeout < 20000 {
					readTimeout = uint(timeout)
				} else {
					readTimeout = 20000
				}
			} else {
				readTimeout = 100
			}
		}
	}

	//	info := fmt.Sprint("Serial=name:", name, " buad:", baud, " parity:")
	//	switch parity {
	//	case serial.PARITY_NONE:
	//		info += "N"
	//	case serial.PARITY_EVEN:
	//		info += "E"
	//	case serial.PARITY_ODD:
	//		info += "O"
	//	}
	//	info += fmt.Sprint(" data bits:", dataBits, " stop bits:", stopBits)
	//	logs.Info(info)
	return
}
